#ifndef __DRAWINGTOOL_H__
#define __DRAWINGTOOL_H__

#include <gtk/gtk.h>
#include <cairo.h>

class DTPOINT 
{
public:
   double X;
   double Y;

public:
   DTPOINT() { X = Y = 0; }
   DTPOINT(double x, double y) { X = x; Y = y; }
};


class DTRECT
{
public:
   double X;
   double Y;
   double width;
   double height;
   
public: // constructors
   DTRECT() { X = Y = width = height = 0; }
   DTRECT(double x, double y, double w, double h)
   {
      X      = x;
      Y      = y;
      width  = w;
      height = h;
   }

public: // operations
   DTRECT& inflate(double spacing);
   DTRECT& inflate(double x, double y, double w, double h);
   DTRECT& moveTo(double x, double y); 
   DTRECT& moveTo(DTPOINT& pt);
   DTRECT& centeredAt(DTPOINT& pt);
   DTRECT& centeredAt(double cx, double cy);
};

class RGBA 
{
public:
  double red;
  double green;
  double blue;
  double alpha;

public:
   RGBA() { red = green = blue = alpha = 0; }
   RGBA(RGBA& rgba) 
   {
      red = rgba.red;
      green = rgba.green;
      blue  = rgba.blue;
      alpha = rgba.alpha;
   }
   RGBA(double r, double g, double b, double a) 
   {
      red = r;
      green = g;
      blue = b;
      alpha = a;
   }

   bool operator != (RGBA& rgba)
   {
      return (red  != rgba.red  || green != rgba.green || 
              blue != rgba.blue || alpha != rgba.alpha);
   }
};

extern RGBA RGBA_DEFAULT;
extern RGBA RGBA_RED;
extern RGBA RGBA_GREEN;
extern RGBA RGBA_BLUE;
extern RGBA RGBA_WHITE;

//////////////////////////////////////////////////////////////
// DrawingTool Class Declaration

enum DRAWINGTOOL_SETTINGS { 
       DTST_FONT_SIZE, 
       DTST_SOURCE_RGBA,
       DTST_LINE_WIDTH,
       DTST_FONT_FACE,
       DTST_COORDINATION_SYSTEM
     };

enum { TEXT_COORDINATION = 1,    // TEXT:       (0,0) is the left-top pointer
       CHART_COORDINATION,       // CHART:      (0,0) is the left-bottom pointer
       MATHEMATIC_COORDINATION   // MATHEMATIC: (0,0) is the center point of the drawing area
     };

//  coordination system:
//    - original point
//    - direction of x and y axis
//    - scale of x and y axis 
//    - angles of x and y axis


class DrawingTool
{
public:
   cairo_t*  cairoObj;
   int       coordination;
   double    height;
   double    width;

protected:
   void map_coordination(double& posX, double& posY, int coord = 0); 
   void map_coordination(DTPOINT& pt, int coord = 0) 
   { 
      map_coordination(pt.X, pt.Y, coord); 
   }
   
   void map_coordination(double& x, double& y, double& w, double& h, int coord);
   void map_coordination(DTRECT& rect, int coord = 0) 
   { map_coordination(rect.X, rect.Y, rect.width, rect.height, coord); }
   void set_coordination_system(int coord)
   {
      cairo_matrix_t   matrix;
        
      if (coord == CHART_COORDINATION) 
      {
         cairo_translate(cairoObj, 0, height); // move the orign point to (0, height)
         cairo_get_matrix(cairoObj, &matrix);

         matrix.yy = -1.0;  // change the y axis direction
         cairo_set_matrix(cairoObj, &matrix);
      }
      else if (coord == MATHEMATIC_COORDINATION) {
         cairo_translate(cairoObj, width / 2, height / 2); // move the orign point to the center
         cairo_get_matrix(cairoObj, &matrix);

         matrix.yy = -1.0;  // change the y axis direction
         cairo_set_matrix(cairoObj, &matrix);
      }
      else if (coord == TEXT_COORDINATION) 
      {
         cairo_translate(cairoObj, 0, 0); // move the orign point to (0, height)
         cairo_get_matrix(cairoObj, &matrix);

         matrix.yy = 1.0;  // change the y axis direction
         cairo_set_matrix(cairoObj, &matrix);
      }
      
      coordination = coord;
      return;
   }
   
public: // constructor and destructor
   DrawingTool() 
   {
      coordination = TEXT_COORDINATION; 
      cairoObj     = NULL;
   }
   
   ~DrawingTool()
   {
      if (cairoObj)  
         cairo_destroy(cairoObj);
   }
   
   // >>>> create() 
   bool create(GtkWidget* widget)
   {
      cairoObj =  gdk_cairo_create(widget->window);
      height   =  widget->allocation.height;
      width    =  widget->allocation.width;
      
      return (cairoObj != NULL); 
   }
   
   bool create(cairo_surface_t* surface) 
   {
      cairoObj = cairo_create(surface);
      height   = cairo_image_surface_get_height(surface);
      width    = cairo_image_surface_get_width(surface);  
      
      return (cairoObj != NULL);
   }

public:// attribute configuration functions
   bool setAttribute(DRAWINGTOOL_SETTINGS propId, double val)
   {
      switch (propId)
      {
      case DTST_LINE_WIDTH:
         cairo_set_line_width(cairoObj, val);
         break;
      case DTST_FONT_SIZE:
         cairo_set_font_size (cairoObj, val);
         break;
      case DTST_COORDINATION_SYSTEM:
         set_coordination_system((int) val); 	      
         break;
      }
      
      return true; 
   }

   bool setAttribute(DRAWINGTOOL_SETTINGS propId, RGBA& rgba)
   {
      switch (propId) 
      {
         case DTST_SOURCE_RGBA:
            cairo_set_source_rgba(cairoObj, rgba.red, rgba.green, rgba.blue, rgba.alpha);
            break;
         default:
	    break;    
      }
   
      return true;
   }
   
   bool getAttribute(DRAWINGTOOL_SETTINGS attrId, double& val)
   {
      switch (attrId) 
      {
         case DTST_LINE_WIDTH:
            val = cairo_get_line_width(cairoObj);
            break;
         case DTST_FONT_SIZE:
            //cairo_get_font_size (cairoObj);
            break;
         case DTST_COORDINATION_SYSTEM:
	         coordination = (int) val;
	         break;
	   }
	   
	   return true;
   }
   
   // >>>
   void setSourceRGBA(RGBA& rgba)
   {
      setSourceRGBA(rgba.red, rgba.green, rgba.blue, rgba.alpha);
   }
   void setSourceRGBA(double r, double g, double b, double a)
   {
      cairo_set_source_rgba(cairoObj, r, g, b, a);
   }

   void setFontSize(double size) { cairo_set_font_size(cairoObj, size); }
   void setCoordinationMapping(int coord)  { coordination = coord; }
   void fill() { cairo_fill(cairoObj); }

public: // DRAWING OPERATIONS
   void moveTo(double x, double y) { cairo_move_to(cairoObj, x, y); }
   void lineTo(double x, double y) { cairo_line_to(cairoObj, x, y); }
   void moveTo(DTPOINT& pt) { moveTo(pt.X, pt.Y); }
   void lineTo(DTPOINT& pt) { lineTo(pt.X, pt.Y); }
   
   bool stroke()
   {
      cairo_stroke(cairoObj);
      return true;
   }
   
   bool selectFont(gchar* szFontName,cairo_font_slant_t nSlant,cairo_font_weight_t  nWeight)
   {
      cairo_select_font_face(cairoObj, szFontName, nSlant, nWeight);
      return true;
   }

   bool showText(gchar* szText)
   {
      bool not_text_mode = (coordination != TEXT_COORDINATION);
      int origin_coordination = coordination;    
      DTPOINT pt1, pt2;
            
      if (not_text_mode) 
      {
         // this is important for developers use.
         cairo_get_current_point(cairoObj, &pt1.X, &pt1.Y);
         pt2 = pt1;
         //map_coordination(pt2, TEXT_COORDINATION);
         set_coordination_system(TEXT_COORDINATION);
         //moveTo(pt2.X, pt2.Y);
      }   
      
      cairo_show_text(cairoObj, szText);
      
      if (not_text_mode) 
      {
         set_coordination_system(coordination);
         //cairo_move_to(cairoObj, pt1.X, pt1.Y);
      }   
         
      return true;
   }
   
   void paint() {  cairo_paint(cairoObj); }

   void rectangle(double x, double y, double w, double h)
   {
      cairo_rectangle(cairoObj, x, y, w, h);
   }

public: // some wrapper function to easy the effort about outputing glyphs.
   void drawRectangle(DTRECT& rect, RGBA& rgba = RGBA_DEFAULT) 
   {
      if (rgba != RGBA_DEFAULT)
	      setSourceRGBA(rgba);

      rectangle(rect.X, rect.Y, rect.width, rect.height);
      stroke();
   }
   void fillRectangle(DTRECT& rect, double x, double y, RGBA& rgba = RGBA_DEFAULT)
   {
      if (rgba != RGBA_DEFAULT)
	      setSourceRGBA(rgba);

      rectangle(rect.X, rect.Y, rect.width, rect.height);
      fill();
   }
   void drawLine(double origX, double origY, double destX, 
                 double destY, RGBA& rgba = RGBA_DEFAULT)
   {
      if (rgba != RGBA_DEFAULT) 
      {
         setSourceRGBA(rgba);
      }

      moveTo(origX, origY);
      lineTo(destX, destY);
      stroke();
      return;
   }
   void drawLine(double destX, double destY, RGBA& rgba = RGBA_DEFAULT)
   {
      if (rgba != RGBA_DEFAULT)
      {
         setSourceRGBA(rgba);
      }

      lineTo(destX, destY);
      stroke();
      return;   
   }
   
   void drawLine(double origX, double origY, double destX, 
                 double destY, double size, RGBA& rgba = RGBA_DEFAULT)
   {
   }
   
   void setSource(cairo_pattern_t* source) { cairo_set_source(cairoObj, source); return; }
   cairo_pattern_t* getSource() { return cairo_get_source(cairoObj); }
   
   void setSource(cairo_surface_t* source, double origX = 0, double origY = 0) 
   { 
      cairo_set_source_surface(cairoObj, source, origX, origY);
      return;
   }
   
   cairo_pattern_t* createPatternFromSurface(cairo_surface_t* surface)
   {
      return cairo_pattern_create_for_surface(surface);
   }
   
   cairo_surface_t* getTarget() { return cairo_get_target(cairoObj); }
};

////////////////////////////////////////////////////////////////////////
// DToolSufafce


#endif


